/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 * 
 * This file is part of Indigo toolkit.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace indigo
{ 
   public unsafe class Dingo : IDisposable
   {
      const string dingodll = "dingo.dll";
      const string obsoleteOptionMessage = 
         "This method is obsolete and may be removed in future releases. Please consider using an appropriate setOption() method instead";

      [DllImport("kernel32")]
      static extern IntPtr LoadLibrary(string lpFileName);
      private long id = -1;

      public static int versionCode()
      {
         return dingoVersionCode();
      }

      public static string versionRevisionString()
      {
         return new String(dingoVersionRevisionString());
      }

      private void init (string prefix)
      {
         string subprefix = (IntPtr.Size == 8) ? "lib64/" : "lib32/";

         LoadLibrary(prefix + "/" + subprefix + "zlib1.dll");
         LoadLibrary(prefix + "/" + subprefix + "libpng12-0.dll");
         if (IntPtr.Size == 8)
         {
            LoadLibrary(prefix + "/" + subprefix + "libgcc_s_1.dll");
         }
         LoadLibrary(prefix + "/" + subprefix + "libcairo-2.dll");
         LoadLibrary(prefix + "/" + subprefix + dingodll);

         id = dingoAllocSessionId();
         reset();
      }

      public Dingo(string prefix)
      {
         init(prefix);
      }

      public Dingo()
      {
         init(".");
      }

      ~Dingo()
      {
         Dispose();
      }

      public void Dispose()
      {
         if (id >= 0) {
            dingoReleaseSessionId(id);
            id = -1;
         }
      }

      private void onError()
      {
         throw new DingoException(new String(dingoGetLastError()));
      }

      /// <summary>
      /// Enable logging of library activities to the file specified
      /// </summary>
      /// <param name="path">path to log file</param>
      public void setLogPath(String path)
      {
         dingoSetSessionId(id);
         dingoBeginLog(path);
      }

      /// <summary>
      /// <para>Set whether to load highlighting from file or not.</para>
      /// <para>To be set before loading.</para>
      /// <para>Default: true</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setLoadHighlighting(bool enabled)
      {
         dingoSetSessionId(id);
         if (dingoSetLoadHighlighting(enabled ? 1 : 0) == 0)
            onError();
      }

      /// <summary>
      /// <para>Load MOLfile or SMILES from file specified</para>
      /// </summary>
      public void loadMoleculeFromFile(String filename)
      {
         dingoSetSessionId(id);
         if (dingoLoadMolFromFile(filename) == 0)
            onError();
      }

      /// <summary>
      /// <para>Load MOLfile or SMILES from memory buffer</para>
      /// </summary>
      public void loadMolecule(byte[] buf)
      {
         dingoSetSessionId(id);
         if (dingoLoadMolFromBuffer(buf, buf.Length) == 0)
            onError();
      }

      /// <summary>
      /// <para>Load MOLfile or SMILES from string</para>
      /// </summary>
      public void loadMolecule(String str)
      {
         dingoSetSessionId(id);
         if (dingoLoadMolFromString(str) == 0)
            onError();
      }

      /// <summary>
      /// <para>Load RXNfile or reaction SMILES from file specified</para>
      /// </summary>
      public void loadReactionFromFile(string filename)
      {
         dingoSetSessionId(id);
         if (dingoLoadReactionFromFile(filename) == 0)
            onError();
      }

      /// <summary>
      /// <para>Load RXNfile or reaction SMILES from memory buffer</para>
      /// </summary>
      public void loadReaction(byte[] buf)
      {
         dingoSetSessionId(id);
         if (dingoLoadReactionFromBuffer(buf, buf.Length) == 0)
            onError();
      }

      /// <summary>
      /// <para>Load RXNfile or reaction SMILES from string</para>
      /// </summary>
      public void loadReaction(String str)
      {
         dingoSetSessionId(id);
         if (dingoLoadReactionFromString(str) == 0)
            onError();
      }

      /// <summary>
      /// <para>Check whether molecule or reaction loaded is empty</para>
      /// </summary>
      public bool isEmpty()
      {
         dingoSetSessionId(id);
         return dingoMoleculeIsEmpty() != 0 && dingoReactionIsEmpty() != 0;
      }

      /// <summary>
      /// <para>Set output format: "png", "pdf" or "svg".</para>
      /// <para><paramref name="format"/> is case-insensitive</para>
      /// </summary>
      public void setOutputFormat(string format)
      {
         dingoSetSessionId(id);
         if (dingoSetOutputFormat(format) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set image size in pixels</para>
      /// </summary>
      public void setImageSize(int width, int height)
      {
         dingoSetSessionId(id);
         if (dingoSetImageSize(width, height) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set image offset.</para>
      /// <para>This method is only intended to be used with HDC mode. </para>
      /// <para>It DOES NOT affect the size of the resulting image!</para>
      /// </summary>
      public void setHDCOffset(int x, int y)
      {
         dingoSetSessionId(id);
         if (dingoSetHDCOffset(x, y) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set average bond length in pixels.</para> 
      /// <para>This value is ignored if the size is set.</para>
      /// </summary>
      public void setBondLength(float length)
      {
         dingoSetSessionId(id);
         if (dingoSetBondLength(length) == 0)
            onError();
      }

      /// <summary>
      /// <para>Margin size is given by the <paramref name="factor"/> multiplied by the average bond length.</para> 
      /// <para>Default value is 1.0.</para> 
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setMarginFactor(float factor)
      {
         dingoSetSessionId(id);
         if (dingoSetMarginFactor(factor) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set file to store the resulting image.</para>
      /// <para>If not set, rendering is done to internal buffer, see <see cref="Dingo.getResult()"/></para>
      /// </summary>
      public void setOutputFile(string filename)
      {
         dingoSetSessionId(id);
         if (dingoSetOutputFile(filename) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set target HDC for raster output</para>
      /// </summary>
      public void setOutputHDC(IntPtr hdc)
      {
         dingoSetSessionId(id);
         if (dingoSetOutputHDC(hdc.ToPointer()) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set target HDC for vector output (windows metafile)</para>
      /// </summary>
      public void setOutputPrintingHDC(IntPtr hdc)
      {
         dingoSetSessionId(id);
         if (dingoSetOutputHDCPrinting(hdc.ToPointer()) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set the relative bond thickness coefficient (<paramref name="rt"/>).</para>
      /// <para>Actual ratio of bond width to its length is 0.03f * <paramref name="rt"/>.</para>
      /// <para>Reasonable values are 1.0f - 2.0f.</para>
      /// <para>Default: 1.0f</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setRelativeThickness(float rt)
      {
         dingoSetSessionId(id);
         if (dingoSetRelativeThickness(rt) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set background color.</para>
      /// <para>If not set, left transparent.</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setBackgroundColor(Color color)
      {
         dingoSetSessionId(id);
         if (color.A == 0) {
            if (dingoSetBackgroundColor(-1, -1, -1) == 0)
               onError();
         } else {
            if (dingoSetBackgroundColor(color.R / 255.0, color.G / 255.0, color.B / 255.0) == 0)
               onError();
         }
      }

      /// <summary>
      /// <para>Set default foreground color.</para>
      /// <para>Default: black.</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setBaseColor(Color color)
      {
         dingoSetSessionId(id);
         if (dingoSetBaseColor(color.R / 255.0, color.G / 255.0, color.B / 255.0) == 0)
            onError();
      }

      /// <summary>
      /// <para>Use colors for elements.</para>
      /// <para>Default: false</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setColoring(bool enabled)
      {
         dingoSetSessionId(id);
         if (dingoSetColoring(enabled ? 1 : 0) == 0)
            onError();
      }

      /// <summary>
      /// <para>Use Chiral flag when appropriate.</para>
      /// <para>Default: false</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setStereoOldStyle(bool enabled)
      {
         dingoSetSessionId(id);
         if (dingoSetStereoOldStyle(enabled ? 1 : 0) == 0)
            onError();
      }

      /// <summary>
      /// Use color for highlighted bonds and atoms.
      /// </summary>
      /// <param name="enabled"></param>
      /// <param name="color">Ignored if <paramref name="enabled"/>=false</param>
      [Obsolete(obsoleteOptionMessage)]
      public void setHighlightColor(bool enabled, Color color)
      {
         dingoSetSessionId(id);
         if (dingoSetHighlightColorEnabled(enabled ? 1 : 0) == 0)
            onError();
         if (enabled)
            if (dingoSetHighlightColor(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f) == 0)
               onError();
      }

      /// <summary>
      /// Use bold for highlighted bonds and atoms.
      /// </summary>
      /// <param name="enabled"></param>
      [Obsolete(obsoleteOptionMessage)]
      public void setHighlightBold(bool enabled)
      {
          dingoSetSessionId(id);
          if (dingoSetHighlightThicknessEnabled(enabled ? 1 : 0) == 0)
              onError();
      }

      /// <summary>
      /// <para>Set color for AAM indices.</para> 
      /// <para>If not set, defaults to black.</para>
      /// </summary>
      /// <param name="color">Color to use</param>
      [Obsolete(obsoleteOptionMessage)]
      public void setAAMColor(Color color)
      {
         dingoSetSessionId(id);
         if (dingoSetAAMColor(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f) == 0)
             onError();
      }

      /// <summary>
      /// <para>Set whether to center a double bond when it has two single bonds attached, 
      /// one or both of which are stereo bonds orinted towards the shared atom</para> 
      /// <para>Default is false</para>
      /// </summary>
      /// <param name="enabled"></param>
      [Obsolete(obsoleteOptionMessage)]
      public void setCenterDoubleBondWhenStereoAdjacent(bool enabled)
      {
         dingoSetSessionId(id);
         if (dingoSetCenterDoubleBondWhenStereoAdjacent(enabled ? 1 : 0) == 0)
            onError();
      }

      /// <summary>
      /// Set atom indices visible.
      /// </summary>
      /// <param name="enabled"></param>
      [Obsolete(obsoleteOptionMessage)]
      public void setAtomIdsVisible(bool enabled)
      {
         dingoSetSessionId(id);
         if (dingoSetAtomIdsVisible(enabled ? 1 : 0) == 0)
            onError();
      }

      /// <summary>
      /// Set bond indices visible.
      /// </summary>
      /// <param name="enabled"></param>
      [Obsolete(obsoleteOptionMessage)]
      public void setBondIdsVisible(bool enabled)
      {
         dingoSetSessionId(id);
         if (dingoSetBondIdsVisible(enabled ? 1 : 0) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set one or more options.</para>
      /// <para>Options are passed as a string of the form &quot;&lt;name1&gt;=&lt;value1&gt;;&lt;name2&gt;=&lt;value2&gt;;...&quot;.</para>
      /// <para>Components of a value are comma-separated, e.g. &quot;base-color=0.0, 1.0, 0.0&quot;.</para>
      /// </summary>
      public void setOptions(string options)
      {
         dingoSetSessionId(id);
         if (dingoSetOptions(options) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set one of the options.</para>
      /// <para>Separate value components with a comma or use type-specific functions, e.g. setOption(&quot;base-color&quot;,&quot;0.0, 1.0, 0.0&quot;).</para>
      /// </summary>
      public void setOption(string name, string value)
      {
         dingoSetSessionId(id);
         if (dingoSetOption(name, value) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set one of the options (size - width and height).</para>
      /// </summary>
      public void setOption(string name, int w, int h)
      {
         dingoSetSessionId(id);
         if (dingoSetOptionSize(name, w, h) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set one of the options (boolean - enabled or disabled).</para>
      /// </summary>
      public void setOption(string name, bool value)
      {
         dingoSetSessionId(id);
         if (dingoSetOptionEnabled(name, value ? 1 : 0) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set one of the options (real).</para>
      /// </summary>
      public void setOption(string name, float value)
      {
         dingoSetSessionId(id);
         if (dingoSetOptionFloat(name, value) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set one of the options (color).</para>
      /// </summary>
      public void setOption(string name, Color value)
      {
         dingoSetSessionId(id);
         if (dingoSetOptionColor(name, value.R / 255.0f, value.G / 255.0f, value.B / 255.0f) == 0)
            onError();
      }

      /// <summary>
      /// <para>Perform rendering</para>
      /// </summary>
      public void render()
      {
         dingoSetSessionId(id);
         if (dingoRender() == 0)
            onError();
      }

      /// <summary>
      /// <para>Perform layout</para>
      /// </summary>
      public void layout()
      {
         dingoSetSessionId(id);
         if (dingoLayout() == 0)
            onError();
      }

      /// <summary>
      /// <para>Reset settings</para>
      /// </summary>
      public void reset()
      {
         dingoSetSessionId(id);
         if (dingoReset() == 0)
            onError();
      }

      /// <summary>
      /// <para>Scenario: create bitmap of the size given and render the image loaded</para>
      /// <para>With background color</para>
      /// </summary>
      public Bitmap renderToBitmap(Color background)
      {
         return renderToBitmapImpl(0, 0, background);
      }

      /// <summary>
      /// <para>Scenario: create bitmap of the size given and render the image loaded</para>
      /// <para>With background color</para>
      /// </summary>
      public Bitmap renderToBitmap(int width, int height, Color background)
      {
         return renderToBitmapImpl(width, height, background);
      }

      /// <summary>
      /// <para>Scenario: create bitmap of the size given and render the image loaded</para>
      /// <para>Transparent</para>
      /// </summary>
      public Bitmap renderToBitmap(int width, int height)
      {
         return renderToBitmapImpl(width, height, Color.Transparent);
      }

      private Bitmap renderToBitmapImpl(int width, int height, Color background)
      {
         dingoSetSessionId(id);
         
         try
         {
            if (width > 0 && height > 0 && dingoSetImageSize(width, height) == 0)
               onError();

            setOption("background-color", background);

            if (dingoSetOutputFormat("png") == 0 ||
               dingoRender() == 0)
               onError();


            byte[] res = getResult();

            MemoryStream stream = new MemoryStream();
            stream.Write(res, 0, res.Length);
            stream.Seek(0, SeekOrigin.Begin);

            return (Bitmap)Image.FromStream(stream);
         }
         catch (Exception ex)
         {
            throw new DingoException(new String(dingoGetLastError()), ex);
         }
         finally
         {
         }
      }

      /// <summary>
      /// <para>Scenario: create metafile of automatically determined size</para>
      /// </summary>
      public Metafile renderToMetafile ()
      {
         return renderToMetafile(-1, -1);
      }

      /// <summary>
      /// <para>Scenario: create metafile of given size</para>
      /// </summary>
      public Metafile renderToMetafile(int width, int height)
      {
         dingoSetSessionId(id);

         Metafile wmf = null;
         try 
         {

            if (dingoSetOutputFormat("wmf") == 0)
               onError();
            if (width >= 0 && height >= 0)
               if (dingoSetImageSize(width, height) == 0)
                  onError();
            if (dingoRender() == 0)
               onError();
               
            byte[] buf = getResult();
            MemoryStream ms = new MemoryStream(buf);
            wmf = new Metafile(ms);
         }
         catch (Exception ex)
         {
            throw new DingoException("Render to Metafile scenario error", ex);
         } 

         return wmf;
      }

      /// <summary>
      /// <para>Get the result of rendering in case of rendering to a buffer</para>
      /// </summary>
      public byte[] getResult()
      {                       
         dingoSetSessionId(id);
         byte* buf;
         int buf_size;
         if (dingoGetResult(&buf, &buf_size) == 0)
            onError();
         byte[] res = new byte[buf_size];
         for (int i = 0; i < buf_size; ++i)
            res[i] = buf[i];
         return res;
      }

      /// <summary>
      /// <para>Set implicit hydrogen show mode: "none", "terminal", 
      /// "hetero", "terminalhetero", "all".</para>      
      /// <para>Label mode is overridden by implicit hydrogen mode, see <see cref="setLabelMode"/>.</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setImplicitHydrogenMode(string mode)
      {
         dingoSetSessionId(id);
         if (dingoSetImplicitHydrogenMode(mode) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set aromatization mode: "none", "aromatize" (or "arom"), "dearomatize" (or "dear", "dearom").</para>
      /// <para>"none" mode keeps the molecule as it is,</para>
      /// <para> while the other two force aromatization and dearomatization respectively.</para>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setAromatization(string mode)
      {
         dingoSetSessionId(id);
         if (dingoSetAromatization(mode) == 0)
            onError();
      }

      /// <summary>
      /// <para>Set atom label show mode: "normal", "forceshow", "forcehide".</para>
      /// <para>In normal mode only terminal and hetero- atom labels are shown.</para>
      /// <para>Label mode is overridden by implicit hydrogen mode!</para>
      /// <para>To have all labels hidden, use</para>
      /// <code>
      ///    <para>&#160;&#160;&#160;setLabelMode("forcehide");</para>
      ///    <para>&#160;&#160;&#160;setImplicitHydrogenMode("none");</para>
      /// </code>
      /// </summary>
      [Obsolete(obsoleteOptionMessage)]
      public void setLabelMode(string mode)
      {
         dingoSetSessionId(id);
         if (dingoSetLabelMode(mode) == 0)
            onError();
      }

      /// <summary>
      /// <para>Store metafile in EMF format</para>
      /// </summary>
      public static void SaveMetafile(Stream stream, Metafile mf)
      {
         IntPtr henh = mf.GetHenhmetafile();
         int size = GetEnhMetaFileBits(henh, 0, null);

         byte[] buffer = new byte[size];

         if (GetEnhMetaFileBits(henh, size, buffer) <= 0)
            throw new SystemException("GetEnhMetaFileBits");

         stream.Write(buffer, 0, buffer.Length);
         stream.Flush();
      }

      /// <summary>
      /// <para>Store metafile in EMF format</para>
      /// </summary>
      public static void SaveMetafile(String path, Metafile mf)
      {
         FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
         SaveMetafile(fs, mf);
         fs.Close();
      }

      [DllImport("gdi32")]
      private static extern int GetEnhMetaFileBits(IntPtr hemf, int cbBuffer, byte[] lpbBuffer);

      [DllImport(dingodll)]
      private static extern int dingoVersionCode();
      [DllImport(dingodll)]
      private static extern sbyte* dingoVersionRevisionString();
      [DllImport(dingodll)]
      private static extern long dingoAllocSessionId();
      [DllImport(dingodll)]
      private static extern void dingoSetSessionId(long id);
      [DllImport(dingodll)]
      private static extern void dingoReleaseSessionId(long id);
      [DllImport(dingodll)]
      private static extern sbyte *dingoGetLastError ();
      [DllImport(dingodll)]
      private static extern int dingoSetLoadHighlighting (int enabled);
      [DllImport(dingodll)]
      private static extern int dingoLoadMolFromFile (String filename);
      [DllImport(dingodll)]
      private static extern int dingoLoadMolFromBuffer(byte[] buf, int buf_size);
      [DllImport(dingodll)]
      private static extern int dingoLoadMolFromString(String str);
      [DllImport(dingodll)]
      private static extern int dingoLoadReactionFromFile(String filename);
      [DllImport(dingodll)]
      private static extern int dingoLoadReactionFromBuffer(byte[] buf, int buf_size);
      [DllImport(dingodll)]
      private static extern int dingoLoadReactionFromString(String str);
      [DllImport(dingodll)]
      private static extern int dingoMoleculeIsEmpty();
      [DllImport(dingodll)]
      private static extern int dingoReactionIsEmpty();
      [DllImport(dingodll)]
      private static extern int dingoSetOutputFormat (string  format);
      [DllImport(dingodll)]
      private static extern int dingoSetImageSize (int width, int height);
      [DllImport(dingodll)]
      private static extern int dingoSetHDCOffset(int x, int y);
      [DllImport(dingodll)]
      private static extern int dingoSetBondLength (float length);
      [DllImport(dingodll)]
      private static extern int dingoSetMarginFactor(float factor);
      [DllImport(dingodll)]
      private static extern int dingoSetOutputFile (string filename);
      [DllImport(dingodll)]
      private static extern int dingoSetOutputHDC(void* hdc);
      [DllImport(dingodll)]
      private static extern int dingoSetOutputHDCPrinting(void* hdc);
      [DllImport(dingodll)]
      private static extern int dingoSetRelativeThickness(float rt);
      [DllImport(dingodll)]
      private static extern int dingoSetBackgroundColor(double r, double g, double b);
      [DllImport(dingodll)]
      private static extern int dingoSetBaseColor(double r, double g, double b);
      [DllImport(dingodll)]
      private static extern int dingoSetImplicitHydrogenMode(string mode);
      [DllImport(dingodll)]
      private static extern int dingoSetColoring (int enabled);
      [DllImport(dingodll)]
      private static extern int dingoSetStereoOldStyle (int enabled);
      [DllImport(dingodll)]
      private static extern int dingoSetLabelMode(string mode);
      [DllImport(dingodll)]
      private static extern int dingoSetAromatization(string mode);
      [DllImport(dingodll)]
      private static extern int dingoSetHighlightThicknessEnabled(int enabled);
      [DllImport(dingodll)]
      private static extern int dingoSetHighlightColorEnabled(int enabled);
      [DllImport(dingodll)]
      private static extern int dingoSetHighlightColor(float r, float g, float b);
      [DllImport(dingodll)]
      private static extern int dingoSetAAMColor(float r, float g, float b);
      [DllImport(dingodll)]
      private static extern int dingoSetCenterDoubleBondWhenStereoAdjacent(int enabled);
      [DllImport(dingodll)]
      private static extern int dingoSetOptions(string options);
      [DllImport(dingodll)]
      private static extern int dingoSetOption(string name, string value);
      [DllImport(dingodll)]
      private static extern int dingoSetOptionSize(string name, int w, int h);
      [DllImport(dingodll)]
      private static extern int dingoSetOptionEnabled(string name, int enabled);
      [DllImport(dingodll)]
      private static extern int dingoSetOptionFloat(string name, float value);
      [DllImport(dingodll)]
      private static extern int dingoSetOptionColor(string name, float r, float g, float b);
      [DllImport(dingodll)]
      private static extern int dingoRender ();
      [DllImport(dingodll)]
      private static extern int dingoReset ();
      [DllImport(dingodll)]
      private static extern int dingoLayout ();
      [DllImport(dingodll)]
      private static extern int dingoGetResult(byte** buf, int* buf_size);
      [DllImport(dingodll)]
      private static extern void dingoBeginLog(string path);
      [DllImport(dingodll)]
      private static extern int dingoSetAtomIdsVisible(int enabled);
      [DllImport(dingodll)]
      private static extern int dingoSetBondIdsVisible(int enabled);
   }
}
